package com.lstimeline.statuschecker;

import java.io.*;

import org.htmlparser.Parser;
import org.htmlparser.util.*;
import org.htmlparser.filters.*;
import org.htmlparser.tags.*;

import com.lstimeline.utility.Validation;

/**
 * Aces2 StatusChecker Logic
 * @author Harry Zhou
 * @version Jun. 2009
 */
public class Aces2Logic extends AbstractStatusChecker {
	protected String host = "https://aces2.lsac.org";
	protected String pathString = "/YourStatus/membership/AppStatIdMe.aspx?guid=";
	
	/**
	 * the default constructor
	 * @param the Aces2 initializing guid
	 */
	public Aces2Logic(String initHost, String initPath) {
		super();
		host = initHost;
		pathString = initPath;
	}
	
	//side effect: setting urlString to logon.aspx
	protected void connectForCookies() {
		//first cookie: ASP.NET_SessionId=a4khyhapix524duwi0yjcw55 (Chicago has one cookie only.)
		//second cookie: BIGipServerAces2_http=1208029356.20480.0000; path=/
		setupHEADConnection(host + pathString);
	    receiveCookies();
	   
	    setupHEADConnection(host + pathString);
	    receiveCookies();
	    
	    //proceed to the logon page
	    pathString = conn.getHeaderField("Location");
	}
	
	//prerequisite: urlString must be pointing to logon.aspx
	protected String getViewState() throws ParserException, IllegalStateException{
		NodeList nodes = null;
		
		setupGETConnection(host + pathString);
		
		parser = new Parser(conn);
		nodes = parser.parse(new CssSelectorNodeFilter("input#__VIEWSTATE"));
		
		InputTag viewStateNode = (InputTag) nodes.elementAt(0);
		
		return viewStateNode.getAttribute("value");
	}
	
	protected String getLogonString(String username, String password) throws ParserException, UnsupportedEncodingException {
		StringBuilder builder = new StringBuilder();
		
		queryBuilder(builder, "__LASTFOCUS", "");
		queryBuilder(builder, "__EVENTTARGET", "");
		queryBuilder(builder, "__EVENTARGUMENT", "");
		queryBuilder(builder, "ctl00$ContentPlaceHolder1$btnlogon", "logon");
		queryBuilder(builder, "__VIEWSTATE", getViewState());
		queryBuilder(builder, "ctl00$ContentPlaceHolder1$txtUsername", username);
		queryBuilder(builder,"ctl00$ContentPlaceHolder1$txtPassword", password);
		// remove the last '&'
		builder.deleteCharAt(builder.length() - 1);
		
		return builder.toString();
	}
	
	//exhaust response to close the connection
	private void dumpResponse() throws IOException {
			conn.getInputStream().close();
			
			/*BufferedReader in = new BufferedReader(
					new InputStreamReader(conn.getInputStream())
			);
			while(in.readLine() != null);
			in.close();*/
	}
	
	//Aces2
	//Post logon information to logon.aspx
	protected void logon(String username, String password, String viewState) throws Exception{
		String logonString = getLogonString(username, password);
		// urlString is now logon.aspx
		// Should carry the ASP.NET_SessionId cookie
		setupPOSTConnection(host + pathString, logonString.length());
		
		// post the login query
		// __VIEWSTATE, Username, Password, etc
		PrintWriter out = new PrintWriter(conn.getOutputStream());
		out.write(logonString);
		out.flush();
		out.close();
		
		//Important: to ignore response so that the connection can be recylcled.
		dumpResponse();
		//there is a server redirection here
	}
	
	protected String getLogoffString() throws ParserException, UnsupportedEncodingException {
		StringBuilder builder = new StringBuilder();
		
		queryBuilder(builder, "__LASTFOCUS", "");
		queryBuilder(builder, "__EVENTTARGET", "");
		queryBuilder(builder, "__EVENTARGUMENT", "");
		queryBuilder(builder, "__VIEWSTATE", getViewState());
		queryBuilder(builder, "ctl00$ContentPlaceHolder1$ddlTerm", "");
		queryBuilder(builder,"ctl00$ContentPlaceHolder1$btnLogOff", "Log+Off");
		// remove the last '&'
		builder.deleteCharAt(builder.length() - 1);
		return builder.toString();
	}
	
	//Retrieve nodes containg the status message
	private NodeList retrieve() throws Exception{
		// TODO Shouldn't be necessary if redirection works properly
		// set urlString to AppOnlineStatus aspx
		pathString = conn.getHeaderField("Location");
		
		//if retrieval failed
		if(pathString == null) {
			return null;
		}
		
		setupGETConnection(host + pathString);
		
		parser = new Parser(conn);
		NodeList nodes = parser.parse(new CssSelectorNodeFilter("span#ctl00_ContentPlaceHolder1_lblCurrentStatus_data"));
		
		return nodes;
	}
	
	//logoff
	private void logoff() throws Exception {
		String logoffString = getLogoffString();
		
		//still AppOnlineStatus.aspx
		setupPOSTConnection(host + pathString, logoffString.length());
		// post the log off query
		PrintWriter out = new PrintWriter(conn.getOutputStream());
		out.write(logoffString);
		out.close();
		
		//Important: to ignore response so that the connection can be recylcled.
		dumpResponse();
	}
	
	/**
	 * Aces2 StatusChecker Logic
	 */
	public String getStatusMessage(String username, String password) {
		//loosely sanitize username and password
		if(!Validation.isAces2LoginWellFormed(username, password)) {
			statusCode = AUTH_FAIL;
			return null;
		}
		
		connectForCookies();
		
		NodeList nodes = null;
		try {
			logon(username, password, getViewState());
			
			nodes = retrieve();
			
			if(nodes == null || nodes.size() == 0 || nodes.elementAt(0).getFirstChild() == null) {
				statusCode = AUTH_FAIL;
				return null;
			} else {
				statusCode = SUCCESS;
				logoff();
				return nodes.elementAt(0).getFirstChild().toHtml();
			}
		} catch (Exception e) {
			statusCode = ERR;
			e.printStackTrace();
			return null;
		}
	}
}
